package com.uniview.lockconfig;

import com.uniview.exception.CommonException;
import com.uniview.utils.TransSeqGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * created by lux
 * redis 分布式锁util
 */
@Component
public class DistributedRedisLock {
    @Autowired
    RedisTemplate redisTemplate;


    /**
     * @desc 加锁(手动版本)
     * @desc (当前可以启一个异步任务lua脚本去检测锁是否过期,
     * @desc  如果没有过期的话就去延时,保证业务逻辑的准确性)
     * @param lockKey 锁Id(必须全局唯一)
     * @param time 默认时间半个小时
     */
    public String tryLock(String lockKey,long time){
        //获取本次请求的唯一lockId
        String lockId = TransSeqGenerator.getId();
        //场景模拟，一个用户发起库存扣减，如果业务逻辑执行的时间很长锁过期了怎么保证业务的准确性
        if(redisTemplate.hasKey(lockKey)){
            throw new CommonException("1234","获取锁失败");
        }
        //锁住这个lockKey,默认时间time
        redisTemplate.opsForValue().set(lockKey,lockId,time);
        return lockId;
    }
    /**
     * @desc 解锁(使用lua脚本示范)
     * @param lockId
     * @param lockKey
     */
    public void deleteLock(String lockId,String lockKey,DefaultRedisScript redisScript){
       //正确示范
        List<String> keyList  = new ArrayList<String>();
        keyList.add(lockKey);
        keyList.add(lockId);
        redisTemplate.execute(redisScript,keyList,new String[]{});
    }
    /**
     * @desc main方法测试
     * 1、如果说当前业务执行的时间很长，然后锁过期了，下一个进来的线程依然可以获得锁，这样还是有两个线程同时拥有锁
     */
    public static void main(String[] args) {
        DistributedRedisLock redisLockUtils = new DistributedRedisLock();
        //获取redis锁
        String lockId = redisLockUtils.tryLock("123",60);
        try {
            //执行业务逻辑
            System.out.println("");
        } catch (Exception e) {
            //执行业务逻辑报错
        } finally {
            //无论如何要释放锁
//            redisLockUtils.deleteLock(lockId,"123",redisScript);
        }
    }
}
